using System;
using System.Collections.Generic;
using System.Linq;
using PingPong.GameEngine.Components;

namespace PingPong.GameEngine
{
	public class GameState
	{
		#region Members
		
		/// <summary>
		/// The teams.
		/// </summary>
		private Lazy<List<Team>> teams;
		/// <summary>
		/// The type of the scoring.
		/// </summary>
		private Lazy<ScoringType> scoringType;
		/// <summary>
		/// The game events.
		/// </summary>
		private Lazy<List<GameEvent>> gameEvents;
		
		#endregion
		
		#region Properties
		
		/// <summary>
		/// Gets the teams.
		/// </summary>
		/// <value>
		/// The teams.
		/// </value>
		private List<Team> TeamList
		{
			get
			{
				return this.teams.Value;
			}
		}

		/// <summary>
		/// Gets the game event list.
		/// </summary>
		/// <value>The game event list.</value>
		private List<GameEvent> GameEventList
		{
			get
			{
				return this.gameEvents.Value;
			}
		}
		
		/// <summary>
		/// Gets the teams.
		/// </summary>
		/// <value>
		/// The teams.
		/// </value>
		public IEnumerable<Team> Teams
		{
			get
			{
				return this.TeamList;
			}
		}

		/// <summary>
		/// Gets the game events.
		/// </summary>
		/// <value>The game events.</value>
		public IEnumerable<GameEvent> GameEvents
		{
			get
			{
				return this.GameEventList;
			}
		}
		
		/// <summary>
		/// Gets the type of the scoring.
		/// </summary>
		/// <value>
		/// The type of the scoring.
		/// </value>
		public ScoringType ScoringType
		{
			get
			{
				return this.scoringType.Value;
			}
		}
		
		#endregion
		
		#region Constructors
		
		/// <summary>
		/// Initializes a new instance of the <see cref="PingPong.GameEngine.GameState"/> class.
		/// </summary>
		public GameState()
		{
			this.teams = new Lazy<List<Team>>(() => new List<Team>());
			this.gameEvents = new Lazy<List<GameEvent>>(() => new List<GameEvent>());
			this.scoringType = new Lazy<ScoringType>(() => ScoringType.None);
		}
		
		#endregion
		
		#region Methods
		
		/// <summary>
		/// Initialize the specified gameDefinition.
		/// </summary>
		/// <param name='gameDefinition'>
		/// Game definition.
		/// </param>
		public GameState Initialize(GameDefinition gameDefinition)
		{
			if (gameDefinition == null)
			{
				throw new ArgumentNullException("gameDefinition");
			}
			
			//TODO: Add loading logic here.
			
			
			return this;
		}
		
		/// <summary>
		/// Adds the team.
		/// </summary>
		/// <returns>
		/// The team.
		/// </returns>
		/// <param name='team'>
		/// Team.
		/// </param>
		public GameState AddTeam(Team team)
		{
			if(team == null)
			{
				throw new ArgumentNullException("team");
			}
			
			this.TeamList.Add(team);
			return this;
		}

		/// <summary>
		/// Adds the team range.
		/// </summary>
		/// <returns>The team range.</returns>
		/// <param name="teams">Teams.</param>
		public GameState AddTeamRange(IEnumerable<Team> teams)
		{
			if(teams == null || !teams.Any())
			{
				throw new ArgumentOutOfRangeException("teams");
			}

			this.TeamList.AddRange(teams);
			return this;
		}
		
		/// <summary>
		/// Removes the team.
		/// </summary>
		/// <returns>
		/// The team.
		/// </returns>
		/// <param name='team'>
		/// Team.
		/// </param>
		public GameState RemoveTeam(Team team)
		{
			if (team == null)
			{
				throw new ArgumentNullException("team");
			}
			
			this.TeamList.Remove(team);
			return this;
		}

		/// <summary>
		/// Adds the game event.
		/// </summary>
		/// <returns>The game event.</returns>
		/// <param name="gameEvent">Game event.</param>
		public GameState AddGameEvent(GameEvent gameEvent)
		{
			if(gameEvent == null)
			{
				throw new ArgumentNullException("gameEvent");
			}

			this.GameEventList.Add(gameEvent);
			return this;
		}

		/// <summary>
		/// Adds the game event range.
		/// </summary>
		/// <returns>The game event range.</returns>
		/// <param name="gameEvents">Game events.</param>
		public GameState AddGameEventRange(IEnumerable<GameEvent> gameEvents)
		{
			if(gameEvents == null || !gameEvents.Any())
			{
				throw new ArgumentOutOfRangeException("gameEvents");
			}

			this.GameEventList.AddRange(gameEvents);
			return this;
		}

		/// <summary>
		/// Removes the game event.
		/// </summary>
		/// <returns>The game event.</returns>
		/// <param name="gameEvent">Game event.</param>
		public GameState RemoveGameEvent(GameEvent gameEvent)
		{
			if(gameEvent == null)
			{
				throw new ArgumentNullException("gameEvent");
			}

			this.GameEventList.Remove(gameEvent);
			return this;
		}

		/// <summary>
		/// Sets the type of the scoring.
		/// </summary>
		/// <returns>The scoring type.</returns>
		/// <param name="scoringType">Scoring type.</param>
		public GameState SetScoringType(ScoringType scoringType)
		{
			this.scoringType = new Lazy<ScoringType>(() => scoringType);
			return this;
		}

		/// <summary>
		/// Progresses the game state.
		/// </summary>
		/// <returns>The state.</returns>
		public GameState ProgressState()
		{
			foreach(var @event in this.GameEvents)
			{
				if(@event.Evaluate())
				{
					foreach(var action in @event.GameActions)
					{
						action.Execute();
					}
				}
			}

			return this;
		}
		
		#endregion
	}
}

